-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Uniform Cylindrical and Spherical Coordinates #914
base: develop
Are you sure you want to change the base?
Conversation
Outstanding Questions and To-do's:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments below. But a bigger picture comment also: would you be willing to chat with myself, Josh, and Luke about what's actually needed here? We were chatting today and we're pretty sure that (at least in a finite volume context) this interface can be simplified. Two things in particular:
- The geometric source terms can be very likely expressed as differences in "areas" or "generalized volumes"
- For prolongation, there are two ways to go. The way we copied from Athena++ uses differences of centroid positions, which requires storing centroids. However, you can also "densitize" the prolongated quantities before and after interpolation. For linear reconstructions, this is equivalent---there aren't that many ways do something physically correct with only 2 degrees of freedom. For higher-order reconstructions, the densitized approach is much cleaner.
- Restriction doesn't need to be changed.
std::string coord_type_str = pin->GetOrAddString("parthenon/mesh", "coord", "cartesian"); | ||
//REMOVE ME | ||
//if (coord_type_str == "cartesian") { | ||
// coord_type = parthenon::uniformOrthMesh::cartesian; | ||
//} else | ||
if (coord_type_str == "cylindrical") { | ||
PARTHENON_THROW(" Please rebuild with -DCOORDINATE_TYPE=UniformCylindrical"); | ||
} else if (coord_type_str == "spherical_polar") { | ||
PARTHENON_THROW(" Please rebuild with -DCOORDINATE_TYPE=UniformSpherical"); | ||
} else { | ||
PARTHENON_THROW("Invalid coord input in <parthenon/mesh>."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if coordinate systems are going to compile-time only, maybe we drop coord from the input deck entirely?
//---------------------------------------- | ||
// Cylindrical+Spherical Conversions | ||
//---------------------------------------- | ||
// TODO |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... is this actually something we need/want the Parthenon coords tooling to provide? My recommendation is one of the following:
- All coords provide functionality to convert between themselves and Cartesian. For UniformCartesian, this is an identity transformation
- Just make this not parthenon's problem.
Probably but not necessarily we want to go with option 1, as that's what's needed for vis tooling and initial conditions in some contexts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
longer discussion below. Resolving this comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I've done and what Shengtai included in his demo, there's a bigger use case to provide spherical and cylindrical conversions in Parthenon since we're likely to deal with physical systems where those coordinates make sense (i.e. galaxy clusters and stars for spherical setups, jets and disks for cylindrical setups). We could leave it up to each of these systems to implement these conversions on their own but I see them repeated so many times elsewhere it might be better to centralize that code in one place.
I'm not set on including these conversions in Parthenon at the moment but still thinking about it. We might also include conversions for vectors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. No I think that makes sense. As I comment below, we may also want it for vis tools. It would be better if, for example, Paraview could automatically visualize simulations done in spherical coordinates. So we might want to standardize some functions that always map from whatever coordinate system we're in to Cartesian.
KOKKOS_INLINE_FUNCTION | ||
void Xtoijk(const Real &x, const Real &y, const Real &z, int &i, int &j, int &k) const { | ||
i = static_cast<int>( | ||
std::floor((x - xmin_[0]) / dx_[0])) + | ||
istart_[0]; | ||
j = (ndim_ > 1) ? static_cast<int>(std::floor( | ||
(y - xmin_[1]) / dx_[1])) + | ||
istart_[1] | ||
: istart_[1]; | ||
k = (ndim_ > 2) ? static_cast<int>(std::floor( | ||
(z - xmin_[2]) / dx_[2])) + | ||
istart_[2] | ||
: istart_[2]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not had good success making the ternary operator vectorize. Maybe we either switch this to using masks or we split it into 3 functions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also this appears to be for cell centers only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation was lifted from an inline function for swarms
, I copied it nearly verbatim. I should change the function description for "cell centered index". I think this is sufficient for
My very outdated experience on Kepler GPUs was that ternary operators had less branch splitting, so I also default to using ternary operators.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implementation was lifted from an inline function for swarms, I copied it nearly verbatim. I should change the function description for "cell centered index". I think this is sufficient for
👍
My very outdated experience on Kepler GPUs was that ternary operators had less branch splitting, so I also default to using ternary operators.
It's not a huge deal either way, but I was thinking of CPUs when I made the comment, not GPUs. My experience is that GPUs are much better at handling ternary/branching than CPUs.
//---------------------------------------- | ||
// Xs: Area averaged positions | ||
//---------------------------------------- | ||
template <int dir, int side> | ||
KOKKOS_FORCEINLINE_FUNCTION Real Xs(const int idx) const { | ||
assert(dir > 0 && dir < 4 && side > 0 && side < 4); | ||
return Xc<dir>(idx); | ||
} | ||
template <int dir, int side> | ||
KOKKOS_FORCEINLINE_FUNCTION Real Xs(const int k, const int j, const int i) const { | ||
assert(dir > 0 && dir < 4 && side > 0 && side < 4); | ||
return Xc<dir>(k,j,i); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure I like the name here.
template <int dir> | ||
KOKKOS_FORCEINLINE_FUNCTION Real Dxc(const int idx) const { | ||
assert(dir > 0 && dir < 4); | ||
if( dir == X1DIR ){ | ||
return r_c_(idx+1) - r_c_(idx); | ||
} else { | ||
return dx_[dir-1]; | ||
} | ||
} | ||
template <int dir> | ||
KOKKOS_FORCEINLINE_FUNCTION Real Dxc(const int k, const int j, const int i) const { | ||
assert(dir > 0 && dir < 4); | ||
switch (dir) { | ||
case X1DIR: | ||
return Dxc<X1DIR>(i); | ||
case X2DIR: | ||
return Dxc<X2DIR>(j); | ||
case X3DIR: | ||
return Dxc<X3DIR>(k); | ||
default: | ||
PARTHENON_FAIL("Unknown dir"); | ||
return 0; // To appease compiler | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should these be if constexpr
s?
//---------------------------------------- | ||
// Geometric Terms (Find better names for these!) | ||
//---------------------------------------- | ||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h2v(const int i) const { return r_c_(i); }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h2f(const int i) const { return Xf<1>(i); }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h31v(const int i) const { return 1.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h31f(const int i) const { return 1.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh2vd1(const int i) const { return 1.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh2fd1(const int i) const { return 1.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh31vd1(const int i) const { return 0.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh31fd1(const int i) const { return 0.0; }; | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h32v(const int j) const { return 1.0; } | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real h32f(const int j) const { return 1.0; } | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh32vd2(const int j) const { return 0.0; } | ||
|
||
KOKKOS_FORCEINLINE_FUNCTION | ||
Real dh32fd2(const int j) const { return 0.0; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These smell to me like coefficients in the christoffel symbols or something similar. I don't think we need them all for finite volumes-type discretizations... especially if we're restricting ourselves to cartesian, spherical, and cylindrical coordinates with uniform coordinate spacing.
|
||
const std::array<Real, 3> &Dx_() const { return dx_; } | ||
|
||
ParArrayND<Real> r_c_, coord_vol_i_, cosphi_c_, sinphi_c_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these are all 1D right? You could use ParArray1D
here probably.
That said, as a first pass, these calculations are only being cached for performance. We could do them without and just compute inline as well. The only reason not to do this is that the trig functions are not transcendental.
@forrestglines my apologies---After a longer discussion I think @lroberts36 @jdolence and I have all convinced ourselves that my above comments are not right:
This is still mostly true, I think, but that doesn't mean all geometry output can be dropped. In particular, the face area argument holds for equations similar to the hydro equations, but not for all conservation laws. Equations with a diffusive, e.g., second derivative, term don't work out as nicely.
I worked out the densitized interpolation vs centroids... I think there's reason to provide centroids---they are not exactly the same as the densitized thing, even in the linear case. My bad on this. Regarding your questions/TODOs:
I think in the "topological element" framework that @lroberts36 developed, these become centroid values based on different topological elements, e.g., Face1 or Face2. We probably want to rethink the naming convention here...
I looked a bit more carefully at this... In conduction and viscosity, I believe these are also expressed more descriptively in other parts of the coordinate machinery. For example, the term (pco_->h2v(i)*pco_->dx2v(j-1)) that shows up in the x2 flux of conduction in Athena++ can be re-expressed as The There is a mathematical formalism for describing these relevant quantities. In particular the In full generality the metric is a symmetric 3x3 matrix, so it has six possible nonzero terms. In Cartesian, spherical, and cylindrical coordinates, it is a diagonal matrix, so it's 3 nonzero numbers. In full generality, the Christoffel symbols are three symmetric matrices, so 18 nonzero numbers. However for the Cartesian, spherical, and cylindrical coordinates, there are at most two nonzero terms.
I could go either way on this. I think one option would be to use the above geometric description, using the metric tensor and christoffel symbols. Then to get to the AthenaPK version of those equations, when you get there, it would be straightforward to write a translation layer to represent them as these One thing I wanted to ask about---What is the team's perspective on what parthenon should support broadly? Should parthenon only support Cartesian, Cylindrical, and Spherical, but provide enough hooks for downstream codes to add more generic objects? Or should we design with more generality in mind? Or should we just restrict ourselves to things that are no more complex than Spherical? One reason I ask is that the geometric language I describe above is dramatically simpler for the special cases we are considering here than in full generality. My lenaing is to support only Cartesian, cylindrical, and spherical in parthenon proper, but try to design with letting downstream codes do more general things if they want.
This may depend on CPU vs GPU. IMO the reason to cache is that
Agreed. Let's just do on-the-fly and see if we need caching at all.
I'd be in favor of that. A more strenuous test might be can we advect a Gaussian blob (with a constant linear velocity) through a spherical/cylindrical coordinate system. I.e., it should still go in a straight line, despite the coordinate system being curved. |
For completeness, the metric tensor for cylindrical coordinates is: The The derivatives of the scale factors contain similar information to the christoffel symbols. The Christoffel symbols in cylindrical coordinates have essentially one nonzero term: |
Oh one final thought: We might want to add functions that transform coordinate positions to Cartesian positions for the purpose of vis tools... And we may also want Jacobians to transform to Cartesian coordinates for visualizing vectors. Those are just for outputs though... not needed in an actual simulation. And they could be encoded instead in, e.g., yt. |
Thank you so much for looking over this PR. Still early stages and we're open to changes/suggestions. I'd be open to chatting if we think there's a need. Talking others in T-2 though, our initial guiding principle is "Make it work like Athena++ first". Since we've generally followed Athena++'s AMR strategy and AthenaPK's hydro implementation follows Athena++, following Athena++'s curvilinear strategy hopefully should be the minimal set of changes. Ideally in the future we'd like swap linear momentum for angular momentum in the conserved variables. I'm not sure how many changes that would require to Parthenon and AthenaPK and might end up in a code separate to AthenaPK. |
Ah, thank you! This was the insight I was hoping for, seeing as I'm newer to using different coordinate systems. Since they're only used in specific use cases, it might be better to spin these off into different objects templated on coordinate system i.e. |
Bit of clarification, in this PR
|
Interesting... I'm not sure, but I think we may want to retain the ability to use coordinate midpoint values in addition to the centroid values. I can see that being valuable in some contexts---for example the volume-weighted interpolation (densitized) if anyone wants to use that. That said maybe that philosophy and the centroid philosophy shouldn't meet. Curious what other folks think about that.
I'm not sure I understand this---my thinking was that a "face-averaged" position would be just the centroid position based at the face topological element. I was thinking there was sort of a generic generalization of the cell centroids for each topological element which matches that definition. What am I missing here? |
r_c_(i) = 0.75*(std::pow(rp, 4) - std::pow(rm, 4)) / | ||
(std::pow(rp, 3) - std::pow(rm, 3)); | ||
x1s2_(i) = TWO_3RD*(std::pow(rp,3) - std::pow(rm,3))/(SQR(rp) - SQR(rm)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Yurlungur here's the two lines where the centroids and so called "face-averaged quantities" differ in spherical coordinates. I'm headed to a lunch meeting so I'll have think about why these two are different later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see---the former is volume integrated, the latter surface-integrated.
I guess then the question is philosophical/API: If I ask for X1 "centroid" value with topological element "face2" do I ever want the volume-averaged version? Or do I always want the face-averaged version? I think the answer is we always want the face-averaged version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that logic, the face position should probably be the face-averaged position.
The other place I can think of where face positions are relevant in AthenaPK is when we define a vector potential on faces then take a centered finite difference curl to get the magnetic field. We'd like the face position to correspond to a divergenceless curl and match the magnetic field we'd like to set as close as possible. I'm not sure if that would mean face-area averaged or cell-volume-averaged for the proper position on the face.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other place I can think of where face positions are relevant in AthenaPK is when we define a vector potential on faces then take a centered finite difference curl to get the magnetic field.
You don't define it on edges? I would think to get a divergence free B-field, you would take the vector potential on edges, curl it, and get B-field on faces?
We'd like the face position to correspond to a divergenceless curl and match the magnetic field we'd like to set as close as possible. I'm not sure if that would mean face-area averaged or cell-volume-averaged for the proper position on the face.
I don't know off the top of my head. Another thing to consider is reconstructions... There you want to use the volume-averaged centroid positions to interpolate to the coordinate centers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for conservative prolongation on volumes, faces, and edges, you always want the centroid coordinates of the topological element itself (not the volume average for faces, for instance). At least at linear order choosing to model your function in the cell as
where
@Yurlungur: Why do you necessarily want to reconstruct to the face coordinate centers instead of the face centroids?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I just wasn't thinking it through. Your argument makes sense to me @lroberts36
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lroberts36 So if I understand correctly, we'd likely always want the position on the face to be the centroid of the face-area, not a centroid based on cell-volume. That's consistent with how Athena++ usually treats face positions although I'm suspicious that they've mixed them up in some place.
@Yurlungur Since we're cell centered the magnetic fields are currently cell-centered. That won't be the case once a CT-MHD scheme makes it into AthenaPK
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@forrestglines: For prolongation, I think it is always true that we want the centroids of the topological element that we are prolongating on (at least for the default prolongation operator).
For reconstruction, we will have cell values and want to reconstruct them to faces (or for some upwind MHD algorithms have face values and want to reconstruct them on edges). The cell averaged value is probably most naturally interpreted as a point-wise value at the cell centroid. Then the reconstructed values on the face will be at two of the cell centroid coordinates and one of the face centroid coordinates, e.g. if I am reconstructing on the face normal to the
All that being said, at least for Cartesian, cylindrical, and spherical coordinates where the radial position only depends on the
Coordinates::Xc<CoordinateDirection dir, TopologicalElement el>(int k, int j, int i);
so that e.g. the reconstructed position of the lower face of a zone normal to the {coords.Xc<X1DIR, TE::CC>(k, j, i), coords.Xc<X2DIR, TE::F2>(k, j, i), coords.Xc<X3DIR, TE::CC>(k, j, i)}
.
[1:] I would be interested to know how/if Athena++ deals with this.
I think it might be worth touching base at some point. I think your strategy makes sense for AthenaPK. From the parthenon perspective, I think we want the minimal feature set that spans AthenaPK's needs and the needs of other downstream codes that might want to use this machinery.
Interesting... That probably requires coordinate transformation matrices (i.e., the Jacobian). There may be some need for that anyway in pgens and vis tools though.
That's an interesting idea. Broadly I think this makes sense. We should think about how that fits into the meshblock and pack machinery though to make sure they're accessible where they're needed. Alternatively, one could imagine caching them on the mesh as variables. Maybe some automated machinery for computing scale factors/christoffels/etc centered at relevant topological elements on the grid? We could just treat them as actual mesh variables? That might cause them to have pretty nonstandard shapes per meshblock though. |
I'm currently working on a private branch of AthenaPK using these constructs in Parthenon. Once I have something partially working I can share that private branch to interested LANL employees and make an advection example in Parthenon. I think we might have enough for cell-centered MHD in this PR already. I'm finding some of the functions in the coordinate class (like the Coord_src terms) might better belong downstream. If we decide to cache these values we'll have to show an example in Parthenon on how to do that. |
Very WIP PR to support uniform cylindrical and spherical coordinates in Parthenon.
PR Summary
Many potential AthenaPK users need treatments of spherical and cylindrical coordinates that can preserve angular momentum. Most of the machinery for these coordinates should belong in Parthenon. This PR is an update of Shengtai's work to match changes to Parthenon's coordinate renaming.
PR Checklist